package anonymouschat.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import anonymouschat.messages.clientrequests.encryptedMessageWrapperMessage;
import anonymouschat.utils.ConstantVariables;

public class ServerIdentObject implements Serializable {

	private static final long serialVersionUID = -2636702683734153948L;
	private PrivateKey privateKey;
	private PublicKey publicKey;
	private byte[] signatureOfPublicKeyByCA; //If a certificate authority signed this server's public key it says that this is a valid (not forged) key
	private String serverName;
	
	public ServerIdentObject(String svrName) throws NoSuchAlgorithmException
	{
		//Generate private and public key here
		System.out.println("Generating new identity file for server: " + svrName);
		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
		gen.initialize(3072);
		KeyPair keyPair = gen.generateKeyPair();
		publicKey = keyPair.getPublic();
		privateKey = keyPair.getPrivate();
        serverName = svrName;
        saveIdentity(); //Save the identity to file so next time we can use it.
	}
	
	public static String getIdentityFileName(String svrName)
	{
		return svrName + "Identity.dat";
	}
	
	public static boolean identityExists(String svrName)
	{
		File f = new File(getIdentityFileName(svrName));
		return f.exists() && f.canRead();
	}
    
    public static ServerIdentObject loadIdentity(String svrName) throws Exception
    {
    	System.out.println("Loading existing identity for server: "+ svrName);
    	ServerIdentObject ident = null;
    	    FileInputStream fin;
			try {
				fin = new FileInputStream(getIdentityFileName(svrName));
			
    	    ObjectInputStream ois = new ObjectInputStream(fin);
    	    ident = (ServerIdentObject) ois.readObject();
    	    ois.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				throw new Exception("Could not load identity file");
			} catch (IOException e) {
				e.printStackTrace();
				throw new Exception("Unable to read serialized server identity object from identity file");
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new Exception("Error occured when casting read in serialized server identity object to ServerIdentObject");
			}
			return ident;
    	 
    }
	
	public PublicKey getPublicKey()
	{
		return publicKey;
	}
	
	public byte[] getsignatureOfPublicKeyByCA()
	{
		return signatureOfPublicKeyByCA;
	}
	
	public Object unwrapEncryptionLayer(encryptedMessageWrapperMessage encryptedMessage) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException
	{
		//TODO: Perform unencryption on encryptedData using privateKey and return
		Cipher dec = Cipher.getInstance(ConstantVariables.ASYMMETRIC_CYPHER_TYPE);
		dec.init(Cipher.DECRYPT_MODE, privateKey);
		
		//byte[] unwrapped = dec.doFinal(encryptedMessage.getencryptedMessage());
		byte[] unwrapped = encryptedMessage.getencryptedMessage();
		ByteArrayInputStream bis = new ByteArrayInputStream (unwrapped);
	    ObjectInputStream ois = new ObjectInputStream (bis);
	    return ois.readObject();
	}
	
    
    public void setPublicKeySignature(byte[] sig)
    {
    	signatureOfPublicKeyByCA = sig;
    }
    
    public void saveIdentity()
    {
    	try {
    	      FileOutputStream fout = new FileOutputStream(getIdentityFileName(serverName));
    	      ObjectOutputStream oos = new ObjectOutputStream(fout);
    	      oos.writeObject(this);
    	      oos.close();

    	      serverEntry publicID = new serverEntry(serverName, InetAddress.getLocalHost().getHostAddress(), Server.getServerPort(), getPublicKey());
    	      fout = new FileOutputStream("public" + getIdentityFileName(serverName));
    	      oos = new ObjectOutputStream(fout);
    	      oos.writeObject(publicID);
    	      oos.close();
    	      
    	      }
    	   catch (Exception e) { e.printStackTrace(); }
    }
	
}
